package prueba3;

import DAO.DAO;
import entidades.Dispositivo;
import java.net.*;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MultiServerThread extends Thread {

    public Socket socket;
    PrintWriter pw;
    int tiempo=4000;
    byte[] buffer = new byte[1500];//tamaño buffer de recepcion
    int Identification = 0;//numero de identificacion del equipo
    int var=0;// Se usa para el segundo envio de la funcion 20
    int cont = 0;// tiempo respuesta de comando, debe modificarse cada vez que cabien el gprs time
    byte IdByte = 0;// Identificacion del equipo en hexa
    boolean SendDataThread = true;//Variable booleana que controla el hilo de envio
    boolean ThisThreadFlag = true;//Variable booleana que controla el hilo de recepcion
    int IdFuncion = 0;//Funcion de identficacion
    boolean Registrado = false;//Bandera de registro en la base de datos
    Protocolo protocolo = new Protocolo();//Objeto protocolo encargado de manejar las comunicaciones
    boolean[] flag = new boolean[24];// Banderas de funcion
    boolean[] flag1 = new boolean[24];// Banderas de funcion auxiliares
    int estado=0;
    int opcion=0;
    String mensaje="";
    boolean bandera=false;


    public MultiServerThread(Socket sockets) {
        socket = sockets;// En este constructor tomamos el socket enviado por socketserver y lo pasamos a un socket convencional.
    }

    private class SendData implements Runnable {

        public void run() {
            DataOutputStream outToClient = null;
            try {
                outToClient = new DataOutputStream((socket.getOutputStream()));
            } catch (IOException ex) {//Si ocurre algun problema se paran los dos hilos
                SendDataThread = false;
                ThisThreadFlag = false;
            }
            byte[] ObtenerGPRSTIME = {IdByte, 3, 0, -74, -1, -1};// Para modificar el time
            try {
                outToClient.write(ObtenerGPRSTIME);
                System.out.println("Funcion 3 Enviada!");
            } catch (IOException ex) {
                SendDataThread = false;
                ThisThreadFlag = false;
            }
            try {
                while (SendDataThread) {// esta bandera permite interrumpir este hilo.
                    Thread.sleep(200);// tiempo que permite que el procesador atienda otros hilos
                     opcion = protocolo.verificarComandos();
                     estado = protocolo.obtenerEstadoComando();
                    //leer si esta en espera
                    if (opcion != 0 && estado==2) {
                        switch (opcion) {
                            case 1:
                                byte[] enviar1 = {IdByte, 1, 0, -66, -1, -1};
                                outToClient.write(enviar1);
                                flag[opcion]=true;//Con la activacion de esta bandera se espera confirmacion de recepcion de la funcion
                                flag1[opcion]=true;
                                System.out.println("Funcion 1 enviada");
                                break;
                            case 2:
                                byte[] enviar2 = {IdByte, 2, 0, 3, -1, -1};
                                outToClient.write(enviar2);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 2 enviada");
                                break;
                            case 3:
                                byte[] enviar3 = {IdByte, 3, 0, -74, -1, -1};
                                outToClient.write(enviar3);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 3 enviada");
                                break;
                            case 4:
                                byte[] enviar4 = {IdByte, 4, 0, 112, -1, -1};
                                outToClient.write(enviar4);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 4 enviada");
                                break;
                            case 6:
                                byte[] enviar6 = {IdByte, 6, 1, 24, -1, -1};
                                outToClient.write(enviar6);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 6 enviada");
                                break;
                            case 7:
                                byte[] enviar7 = {IdByte, 7, 1, 4, -1, -1};
                                outToClient.write(enviar7);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 7 enviada");
                                break;
                            case 10:
                                byte[] enviar10 = protocolo.FuncionDiezEnv(IdByte);
                                outToClient.write(enviar10);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 10 enviada");
                                break;
                            case 23://funcion 10 parte 2
                                byte[] enviar102 = protocolo.FuncionDiezEnv2(IdByte);
                                outToClient.write(enviar102);
                                opcion=10;
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 10 parte 2 enviada");
                                break;
                            case 11:
                                byte[] enviar11 = protocolo.FuncionOnceEnv(IdByte);
                                outToClient.write(enviar11);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 11 enviada");
                                break;
                            case 12:
                                byte[] enviar12 = protocolo.FuncionDoceEnv(IdByte);
                                outToClient.write(enviar12);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 12 enviada");
                                break;
                            case 13:
                                byte[] enviar13 = protocolo.FuncionTreceEnv(IdByte);
                                outToClient.write(enviar13);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 13 enviada");
                                break;
                            case 14:
                                byte[] enviar14 = protocolo.FuncionCatorceEnv(IdByte);
                                outToClient.write(enviar14);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 14 enviada");
                                break;
//                            case 20:
//                                
//                                byte[] enviar20 = protocolo.FuncionVeinteEnv(IdByte);
//                                outToClient.write(enviar20);
//                               
//                                flag[opcion]=true;
//                                flag1[opcion]=true;
//                                System.out.println("Funcion 20 enviada");
//                                break;
                            case 21:
                                byte[] enviar21 = protocolo.FuncionVeintiunoEnv(IdByte);
                                outToClient.write(enviar21);
                                flag[opcion]=true;
                                flag1[opcion]=true;
                                System.out.println("Funcion 21 enviada");
                                break;
//                            case 22:
//                                byte[] enviar22 = protocolo.FuncionVeintidosEnv(IdByte);
//                                outToClient.write(enviar22);
//                                flag[opcion]=true;
//                                flag1[opcion]=true;
//                                System.out.println("Funcion 22 enviada");
//                                break;
                            default:
                                break;

                        }
                        while (flag[opcion] == true && cont <40 && SendDataThread==true) {//Se esperan 40 segundos antes de poner el estado del comando en fallo
                            Thread.sleep(1000);
                            System.out.println("esperando a que llegue info del g4500 "+ cont);
                            cont++;
                        }
                        if(cont==40){
                        protocolo.comandoFallo();
                        bandera= true;
                        flag[opcion]=false;
                        }
                        cont=0;
                        // si se puso en  3 es decir fallo no esperar solo si se recibio la info
                         while (flag1[opcion] == true && cont <200 && SendDataThread==true && bandera==false) {//Este es el tiempo que se estima para que se hagan los cambios en BD
                            Thread.sleep(1000);
                            cont++;
                             System.out.println("esperando "+ cont);
                        }
                        cont=0;
                        bandera=false;
                        System.out.println("tiempo de espera antes de realizar una nueva lectura de la base de datos");
                        Thread.sleep(4000);
                        System.out.println("despues de tiempo de espera 4 segundos");
                        flag1[opcion]=false;
                    }
                }

            } catch (IOException ex) {
                SendDataThread = false;// cerramos los sockets si determinamos un error en el envio de datos
                ThisThreadFlag = false;
            } catch (InterruptedException e) {
                SendDataThread = false;// solo termina este hilo
            }
            try {
                outToClient.close();// cuando se hace esto se interrumpe el hilo de envio asi este en espera de datos. Hasta el momento solo pasa ante time out
                ThisThreadFlag = false;
                socket.close();
            } catch (IOException ex) {
                ThisThreadFlag = false;
            }
            System.out.println(Thread.currentThread().getName()+"Fin de hilo de envio del dispositivo "+ Identification);
        }
    }

    @Override
    public void run() {
        FileWriter a = null;
        
        
        //La inicializacion de la variables en false indica que no hay funciones pendientes
        flag[0]=false;
        flag[1]=false;
        flag[2]=false;
        flag[3]=false;
        flag[4]=false;
        flag[5]=false;
        flag[6]=false;
        flag[7]=false;
        flag[8]=false;
        flag[9]=false;
        flag[10]=false;
        flag[11]=false;
        flag[12]=false;
        flag[13]=false;
        flag[14]=false;
        flag[20]=false;
        flag[21]=false;
        flag[22]=false;
        flag[23]=false;// Bandera funcion 10 parte 2

        flag1[0]=false;
        flag1[1]=false;
        flag1[2]=false;
        flag1[3]=false;
        flag1[4]=false;
        flag1[5]=false;
        flag1[6]=false;
        flag1[7]=false;
        flag1[8]=false;
        flag1[9]=false;
        flag1[10]=false;
        flag1[11]=false;
        flag1[12]=false;
        flag1[13]=false;
        flag1[14]=false;
        flag1[20]=false;
        flag1[21]=false;
        flag1[22]=false;
        flag[23]=false;// bandera funcion 10 parte 2
        //Creacion del InputStream
        try {
            socket.setSoTimeout(1800000);//Se ajusta el tiempo maximo de espera en media hora
            DataInputStream inFromClient = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
            int number = inFromClient.read(buffer);//Se espera el primer envio de datos
            if (number > 0) {
                byte[] bufferAjustado = new byte[number];//Creacion de un buffer con la extension del comando recibido
                System.arraycopy(buffer, 0, bufferAjustado, 0, number);// copia la informacion de buffer a bufferAjustado
                Identification = protocolo.ObtenerId(bufferAjustado);//Identificador  en decimal se efectua una comprobacion basica de la trama de datos recibida
                IdByte = bufferAjustado[0];// Identficacion del dispositivo en Hexadecimal
                try {
                    a = new FileWriter(String.valueOf(Identification) +".txt");
                    pw = new PrintWriter(a);
                } catch (IOException ex) {
                    Logger.getLogger(MultiServerThread.class.getName()).log(Level.SEVERE, null, ex);
                }
                if (Identification != 0) {
                    Registrado = protocolo.IdentificarBD(Identification);
                    if (Registrado == true) {
                       protocolo.RegistarBitacoraConexion();
                        System.out.println("Registro exitoso");
                        if(protocolo.obtenerEstadoComando()==2){
                        protocolo.comandoFallo();// si existe un comando pendiente en el momento en el que se crea una nueva conexion este pasa a fallo de forma inmediata
                        }
                        protocolo.terminarComando();
                        new Thread(new SendData()).start();//Creacion del hilo de envio de datos
                        number = 0;// Numero indica la cantidad de bytes leidos si es igual a -1 significa que la conexion fianlizó por parte del host
                        while (number != -1 && ThisThreadFlag) {
                            try {
                                number = inFromClient.read(buffer);//Espera llegada de datos
                                System.out.println("datos recibidos");
                                if (number > 0) {                                    
                                    bufferAjustado = new byte[number];
                                    System.out.println("el numero bytes leidos es" + number);
                                    System.arraycopy(buffer, 0, bufferAjustado, 0, number);// con esto se envia solo la iformacion util hacia la interfaz grafica
                                    IdFuncion = protocolo.IdentificarFuncion(bufferAjustado);
                                    System.out.println("funcion recibida: " + IdFuncion);
                                    switch (IdFuncion) {
                                        case 1:// colocar que solo se haga eso si la bandera esta en true por si los datos llegan depues de que pasa el tiempo de comando que esta en while del hilo de envio
                                            System.out.println("llegaron datos funcion 1");
                                            flag[IdFuncion]=false;
                                            protocolo.FuncionUno(bufferAjustado);
                                            protocolo.comandoExitoso();
                                            //Significa que se respondió a la función enviada
                                            break;
                                        case 2:
                                            System.out.println("llegaron datos funcion 2");
                                            flag[IdFuncion]=false;
                                            protocolo.FuncionDos(bufferAjustado);
                                            protocolo.comandoExitoso();
                                            break;
                                        case 3:
                                            System.out.println("llegaron datos funcion 3");
                                            flag[IdFuncion]=false;
                                            protocolo.FuncionTres(bufferAjustado);
                                            if(protocolo.GPRS_Time<10)
                                                socket.setSoTimeout(10*3*1000);
                                            else
                                                socket.setSoTimeout(protocolo.GPRS_Time*3*1000);
                                            protocolo.comandoExitoso();
                                            break;
                                        case 4:
                                            System.out.println("llegaron datos funcion 4");
                                            flag[IdFuncion]=false;
                                            protocolo.FuncionCuatro(bufferAjustado);
                                            protocolo.comandoExitoso();
                                            break;
                                        case 5:
                                            protocolo.FuncionCinco(bufferAjustado);
                                            break;
                                        case 6:
                                             System.out.println("llegaron datos funcion 6");
                                             flag[IdFuncion]=false;
                                            protocolo.FuncionSeis(bufferAjustado);
                                            protocolo.comandoExitoso();
                                            break;
                                        case 7:
                                             System.out.println("llegaron datos funcion 7");
                                             flag[IdFuncion]=false;
                                            protocolo.FuncionSiete(bufferAjustado);
                                            protocolo.comandoExitoso();
                                            break;
                                        case 10:
                                            System.out.println("Confirmacion funcion 10");
                                            flag[IdFuncion]=false;
                                            protocolo.comandoExitoso();
                                            break;
                                        case 11:
                                            System.out.println("Confiarmcion funcion 11");
                                            flag[IdFuncion]=false;
                                            if(protocolo.GPRS_Time<10)
                                                socket.setSoTimeout(10*3*1000);
                                            else
                                                socket.setSoTimeout(protocolo.GPRS_Time*3*1000);
                                            protocolo.comandoExitoso();
                                            break;
                                        case 12:
                                            System.out.println("confirmacion funcion 12");
                                            flag[IdFuncion]=false;
                                            protocolo.comandoExitoso();
                                            break;
                                        case 13:
                                            System.out.println("confirmacion funcion 13");
                                            flag[IdFuncion]=false;
                                            protocolo.comandoExitoso();
                                            break;
                                        case 14:
                                            System.out.println("confirmacion 14");
                                            flag[IdFuncion]=false;
                                            protocolo.comandoExitoso();
                                            break;
                                        case 20:
                                            mensaje= new String(bufferAjustado);
                                            System.out.println("los datos recibidos son "+mensaje);
                                            if (mensaje.contains("opened")){
                                                System.out.println("Archivo abierto");
                                                protocolo.terminarComando();
                                                protocolo.setComando();//Activa la funcion 21
                                            }
                                            else{
                                                if (mensaje.contains("exist")) {
                                                System.out.println("Archivo no encontrado");
                                                protocolo.comandoExitoso();
                                                }
                                            }
                                            flag[IdFuncion]=false;
                                            break;
                                        case 21:
                                                if(number>600){
                                                String texto="";
                                                byte[] bufferAjustado2 = new byte[number-2];
                                                System.arraycopy(bufferAjustado, 2, bufferAjustado2, 0, number-2);
                                                texto=new String(bufferAjustado2);
                                                System.out.println(texto);
                                                pw.print(texto);
                                                pw.flush();
                                                System.out.print("intento escribir");
                                                flag[IdFuncion]=false;
                                                }
                                                else{
                                                pw.close();
                                                a.close();
                                                flag[IdFuncion]=false;
                                                tiempo=4000;
                                                protocolo.setComandod();
                                                }
                                            break;
                                        case 22:
                                            flag[IdFuncion]=false;
                                            protocolo.comandoExitoso();
                                            Dispositivo disp=(Dispositivo)protocolo.FuncionVeintidos(Identification);
                                            ManejoArchivo prueba =new ManejoArchivo(disp);
                                            prueba.captutarDatos(Identification+".txt");
                                            prueba.close();
                                            break;
                                        default:
                                            break;
                                    }
                                    Thread.sleep(2000);
                                    flag1[IdFuncion]=false;
                                }
                               
                            } catch (InterruptedException ex) {
                                Logger.getLogger(MultiServerThread.class.getName()).log(Level.SEVERE, null, ex);
                            } catch (SocketTimeoutException e)// si se cumple el Timeout sale de la aplicacion
                            {
                                number = -1;
                                System.out.println(Thread.currentThread().getName()+"Entro en la excepcion del time out");
                                SendDataThread = false;
                                socket.close();
                                //protocolo.close();
                            }
                        }// finalizacion del while
                    } else {// Si la autenticacion ante la base de datos no fue exitosa se cierra el programa
                        inFromClient.close();
                        socket.close();
                        ThisThreadFlag = false;
                        SendDataThread = false;
                        //protocolo.close();
                    }
                } else {// Si la identificacion del dispositivo no es válida es decir su id es negativo o el numero de la funcion sobrepasa el 22 quiere decir que no es una trama enviada por un controlador G4500
                    inFromClient.close();
                    socket.close();
                    SendDataThread = false;
                    ThisThreadFlag = false;
                    //protocolo.close();// aca se pueden cerrar la conexiones porque se asegura que ninguna funcion de protocolo siquira empezo y nunca se creo el hilo de envio de datos
                }// curso normal del programa cuado finaliza el while o si la identificacion del dispositivo no fue la adecuada
                inFromClient.close();
                socket.close();
                SendDataThread = false;
                ThisThreadFlag = false;
            }
        } catch (IOException ex) {// Esta excepcion ocurre ante un problema genral en el socket Por ejemplo cuando se cierra el canal de envio tambien lo hace el de recpcion de esta forma se interrumpe el hilo de recepcion desde el de envio de lo contrario el hilo de recepcion solo terminaria ante el socket time out
            ThisThreadFlag = false;
            System.out.println(Thread.currentThread().getName()+"error en la aplicacion termina el hilo");// a esta parte estaba entrando debido a que el dataoutputsstream se cerraba en la parte del hilo de envio y el input stream se cerraba
            System.out.println("la execepcion es"+ex);
            SendDataThread = false;
            ThisThreadFlag = false;// se supone que despues de una excpecion se ejecuta el codigo que sucede al bloque
            try {// se intenta esto por precaucion para cerrar las conexiones con la base de datos
                Thread.sleep(5000);
                // tal vez sea necesario cerrar tembien el socket y los streams de entrada y salida aunque estas acciones ya fueron definidas cuando finaliza el while o falla cualquier otro proceso
            } catch (InterruptedException ex1) {
                Logger.getLogger(MultiServerThread.class.getName()).log(Level.SEVERE, null, ex1);
            }
            //protocolo.close();
        }  
        
        try {
            socket.close();
        } catch (IOException ex) {
            System.out.println("Fallo el cierre del socket o ya fue cerrado");
            System.out.println("la execepcion es"+ex);
        }

        try {
            if(Identification!=0)
            //protocolo.RegistarBitacoraDesconexion();
            Thread.sleep(15000);
            protocolo.close();
            System.out.println("Cerre conexion a base de datos");
        } catch (InterruptedException ex) {
            Logger.getLogger(MultiServerThread.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("No se cerro conexion a base de datos");
        }
        
        // Necesario esperar un tiempo antes de cerrar las conexiones a la base de datos que es lo que se hace con el la funcion protocolo.close ya que los hilo de recepcion y envio no terminan inmmediatamente y alguno puede hacer una peticion a base de datos lo que se convierte en una conexion que va a permanecer abierta
        System.out.println(Thread.currentThread().getName()+"Fin del hilo de recepcion del dispositivo "+Identification);
    }
}
